/************************************
 * Package: RGcpp					*
 * Authors: Rui Eduardo Gouveia Gil	*
 * Date: 2010						*
 ************************************/
#include "rgcpp/sys/JobScheduler.h"

namespace rgcpp
{

JobScheduler::JobScheduler(unsigned int threadNum) : num(threadNum), exeNum(0), jobSequenceId(1)
{
	mThreads = new boost::thread*[num];

	running = true;

	for(unsigned int i=0; i<num; i++)
		mThreads[i] = new boost::thread(boost::ref(*this));

	boost::mutex::scoped_lock lock(mut);
	if(exeNum!=num)
		cond.wait(lock);

	LOG_INFO_STR("JobScheduler created");

}

JobScheduler::~JobScheduler()
{
	running = false;
	waitingQueue.release();

	for(unsigned int i=0; i<num; i++)
	{
		mThreads[i]->join();
		delete mThreads[i];
	}

	delete mThreads;

	LOG_INFO_STR("JobScheduler deleted");

}

unsigned int JobScheduler::scheduleJob(boost::shared_ptr<Job> job)
{
	boost::mutex::scoped_lock lock(mut);

	job->setJobId(this->jobSequenceId.getNextSequenceId());
	job->setJobScheduler(this);

	LOG_DEBUG_DATA("JobScheduler - New job scheduled. JobId=" << job->getJobId());

	this->jobMap[job->getJobId()] = job;
	this->waitingQueue.addJob(job);

	return job->getJobId();
}

void JobScheduler::rescheduleJob(unsigned int jobId)
{
	boost::mutex::scoped_lock lock(mut);

	if(this->jobMap.find(jobId) == this->jobMap.end())
	{
		Throw(NoSuchElementException("Job not found..."));
	}

	boost::shared_ptr<Job> job = this->jobMap[jobId];

	job->setStatus(JS_WAITING);

	this->waitingQueue.addJob(job);
}

boost::shared_ptr<Job> JobScheduler::getJob(unsigned int jobId)
{
	boost::mutex::scoped_lock lock(mut);

	if(this->jobMap.find(jobId) == this->jobMap.end())
	{
		Throw(NoSuchElementException("Job not found..."));
	}

	return this->jobMap[jobId];
}

void JobScheduler::removeJob(unsigned int jobId)
{
	boost::mutex::scoped_lock lock(mut);

	this->jobMap.erase(jobId);
}

void JobScheduler::operator()()
{
	{boost::mutex::scoped_lock lock(mut);++exeNum;}
	int threadNum = exeNum;
	boost::shared_ptr<Job> job;
	boost::shared_ptr<Job> rootJob;
	LOG_DEBUG_DATA("JobScheduler - Thread "<< threadNum << "/" << num << " started");

	if(exeNum==num)
		cond.notify_one();

	while(true)
	{
		try
		{
			job.reset();
			if(!running) return;
			job = this->waitingQueue.getJob();
			if(!running) return;

			if(job.get() == NULL)
				continue;

			rootJob = job;
			while(rootJob->getParent().get() != NULL)
				rootJob = job->getParent();

			if(this->jobMap.find(rootJob->getJobId()) == this->jobMap.end())
				continue;

			LOG_TRACE_DATA("JobScheduler - Thread(" << threadNum << "): starting executing job(" << job->getJobId() << ")");

			job->executeJob();
			LOG_TRACE_DATA("JobScheduler - Thread(" << threadNum << "): job(" << job->getJobId() << ") successfully executed, waiting=" << (job->pendingJobs()?"true":"false"));

			if(!job->pendingJobs())
			{
				job->notifyParent();
				LOG_TRACE_DATA("JobScheduler - Thread(" << threadNum << "): job(" << job->getJobId() << ") final");
			}
		}
		catch(Exception& e)
		{
			LOG_ERROR_DATA("JobScheduler -  Thread(" << threadNum << "):" << "\n" << e.getStackTrace());
			try{job->notifyParent(JS_FAIL);}catch(...){}
		}
		catch(std::exception& e)
		{
			LOG_ERROR_DATA("JobScheduler -  Thread(" << threadNum << "):" << e.what());
			try{job->notifyParent(JS_FAIL);}catch(...){}
		}
		catch(...)
		{
			LOG_ERROR_DATA("JobScheduler -  Thread(" << threadNum << "): exception");
			try{job->notifyParent(JS_FAIL);}catch(...){}
		}
	}

}

void JobScheduler::scheduleChildJob(unsigned int parentId, boost::shared_ptr<Job> job)
{
	boost::mutex::scoped_lock lock(mut);

	if(this->jobMap.find(parentId) == this->jobMap.end())
	{
		Throw(NoSuchElementException("Parent job not found..."));
	}

	boost::shared_ptr<Job> parent = this->jobMap[parentId];

	job->setJobId(this->jobSequenceId.getNextSequenceId());
	job->setJobScheduler(this);
	job->setParent(parent);

	parent->addChild(job);

	this->waitingQueue.addJob(job);
	LOG_DEBUG_DATA("JobScheduler - New job scheduled. JobId=" << job->getJobId() << " --> ParentId=" << parentId);
}

}
